use std::cell::{RefCell, Ref, RefMut};
use std::collections::BTreeMap;

use crate::kvdb::traits::{Table, Tables, DBError};

use crate::kvdb::mock_table::MockTable;


pub struct MockTables {
    map : BTreeMap<String, Box<RefCell<dyn Table>>>
}
impl MockTables {
    pub fn new() -> MockTables {
        MockTables {
            map : BTreeMap::new()
        }
    }
}
impl Tables for MockTables {

    fn create(&mut self, name: &str) -> Result<(), DBError>{
        if self.map.contains_key(name) {
            return Err(DBError::TableExist);
        }

        self.map.insert(name.to_string(), Box::new(RefCell::new(MockTable::new(name))));
        Ok(())
    }

    fn get(&self, name: &str) -> Option<Ref<dyn Table>>{
        match self.map.get(name) {
            Some(x) => Some(x.borrow()),
            None => None,
        }
    }
    fn get_mut(&mut self, name: &str) -> Option<RefMut<dyn Table>>{
        match self.map.get(name) {
            Some(x) => Some(x.borrow_mut()),
            None => None,
        }
    }

    fn drop(&mut self, name: &str) -> Result<(), DBError>{
        self.map.remove(name)
            .map_or_else(|| Err(DBError::TableNotExist),
                |_| Ok(())
            )
    }

    fn len(&self) -> usize{
        self.map.len()
    }
}

#[cfg(test)]
mod test {

    use super::*;

    #[test]
    fn test_mock_tables(){
        let mut tables = MockTables::new();
        assert_eq!(0, tables.len());

        assert!(tables.create("hello").is_ok());
        assert_eq!(1, tables.len());
        assert_eq!(DBError::TableExist, tables.create("hello").err().unwrap());

        assert!(tables.exist("hello"));

        {
            let tabref = tables.get("hello").unwrap();
            assert_eq!("hello", tabref.name());
            assert_eq!(0usize, tabref.len());
        }
        {
            let mut tabref = tables.get_mut("hello").unwrap();
            assert_eq!("hello", tabref.name());
            assert_eq!(0usize, tabref.len());
            //insert & get
            assert!(tabref.insert(b"hello", b"world").is_ok());
            assert_eq!(1usize, tabref.len());
            assert_eq!(b"world", tabref.get(b"hello").unwrap().unwrap().as_slice());

            //update & get 
            assert!(tabref.update(b"hello", b"world1").is_ok());
            assert_eq!(1usize, tabref.len());
            assert_eq!(b"world1", tabref.get(b"hello").unwrap().unwrap().as_slice());

            //replace & get
            assert!(tabref.update(b"hello", b"world2").is_ok());
            assert_eq!(1usize, tabref.len());
            assert_eq!(b"world2", tabref.get(b"hello").unwrap().unwrap().as_slice());
                
            //remove & exist
            assert!(tabref.remove(b"hello").is_ok());
            assert!(false == tabref.exist(b"hello"));
            assert_eq!(0usize, tabref.len());


        }

        assert!(tables.drop("hello").is_ok());
        assert_eq!(DBError::TableNotExist, tables.drop("hello").err().unwrap());
    }




    
    trait Addr {
        fn print(&self) ;
    }

    struct MyAddr {
        addr: String
    }
    impl MyAddr {
        fn new(addr: &str) -> Self{
            Self {
                addr: addr.to_string()
            }
        }
    }
    impl Addr for MyAddr {
        fn print(&self) {
            println!("MyAddr is : {}", self.addr);
        }
    }



    struct Person {
        name: String,
        addr: Box<dyn Addr>,
    }
    impl Person {
        fn new(name: &str, addr: &str) -> Self {
            Self {
                name: name.to_string(),
                addr: Box::new(MyAddr::new(addr)),
            }
        }
        fn addr(&self) -> AddrRef<'_> {
            AddrRef::new(self.addr.as_ref())
        }
        //fn addr(&mut self) -> AddrRefMut<'_> {
        //    AddrRefMut::new(self.addr.as_mut())
        //}
        
    }
    impl Person {
        fn full_name(&self) -> FullName<'_> {
            FullName::new(&(self.name))
        }
    }

    struct AddrRef<'a> {
        addr: &'a (dyn Addr + 'a),
    }
    impl <'a> AddrRef <'a> {
        fn new(addr: &'a (dyn Addr + 'a)) -> Self {
            Self {
                addr,
            }
        }
    }
    impl <'a> AsRef<(dyn Addr + 'a) > for AddrRef<'a> {
        fn as_ref(&self) -> & (dyn Addr + 'a)  {
            self.addr
        }
    }



    struct FullName<'a> {
        name: &'a String,
    }
    impl <'a> FullName <'a> {
        fn new(name: &'a String) -> Self {
            Self {
                name
            }
        }
    }
    use std::fmt::{Debug, Formatter};
    impl Debug for FullName<'_> {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            writeln!(f, "{}", self.name)
        }
    }

    #[test]
    fn test_syntax_lifte_object(){
        let p = Person::new("hello", "PuDong");
        println!("debug:{:?}", p.full_name());
    }

    

    #[test]
    fn test_syntax_dyn_object(){
        let p = Person::new("hello", "PuDong");
        p.addr().as_ref().print();
    }
}

